package com.chengqs.summer.context;

import com.chengqs.summer.exception.BeanCreationException;
import jakarta.annotation.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * BeanDefinition 类在 IoC 容器中扮演着至关重要的角色，它负责描述 Bean 的各种属性和行为，包括如何实例化、初始化和销毁 Bean。
 * 在实际开发中，理解和使用 BeanDefinition 类可以帮助开发者更好地控制 Bean 的生命周期和行为。
 * 通过提供详细的元数据，该类支持灵活的 Bean 定义和管理机制，是实现 IoC 容器的重要基础。
 * <p>
 * 本类应用了以下设计模式：
 * <ul>
 *     <li><b>Builder Pattern（建造者模式）</b>:
 *     通过提供多个构造方法和配置方法来逐步构建 `BeanDefinition` 对象。这使得在创建 `BeanDefinition` 时，可以根据需要设置不同的属性，从而灵活配置 Bean。</li>
 *
 *     <li><b>Factory Method Pattern（工厂方法模式）</b>:
 *     支持通过工厂方法 (`factoryMethod`) 创建 Bean 实例。这种设计允许在运行时动态决定实例化 Bean 的方式，而不是直接在代码中创建实例。</li>
 *
 *     <li><b>Singleton Pattern（单例模式）</b>:
 *     管理 Bean 的单个实例，通过 `instance` 字段来确保每个 Bean 在容器中只有一个实例。这种方式有助于集中管理 Bean 实例，保持一致性。</li>
 *
 *     <li><b>Strategy Pattern（策略模式）</b>:
 *     支持动态选择和执行不同的初始化 (`initMethod`) 和销毁 (`destroyMethod`) 方法。这使得 Bean 的生命周期管理更加灵活，可以根据需求应用不同的策略。</li>
 *
 *     <li><b>Comparator Pattern（比较器模式）</b>:
 *     实现了 `Comparable` 接口，并重写了 `compareTo` 方法，以支持对 `BeanDefinition` 对象进行排序。这允许根据 `order` 和 `name` 字段来控制 Bean 的加载顺序。</li>
 * </ul>
 * </p>
 */
public class BeanDefinition implements Comparable<BeanDefinition>{

    // 全局唯一的Bean Name:
    private final String name;
    // Bean的声明类型:
    private final Class<?> beanClass;
    // Bean的实例:
    private Object instance = null;
    // 构造方法/null:
    private final Constructor<?> constructor;
    // 工厂方法名称/null:
    private final String factoryName;
    // 工厂方法/null:
    private final Method factoryMethod;
    // Bean的顺序:
    private final int order;
    // 是否标识@Primary:
    private final boolean primary;

    private String initMethodName;
    private String destroyMethodName;

    private Method initMethod;
    private Method destroyMethod;

    /**
     * Component注解创建的BeanDefinition
     * @param name Bean 名称。
     * @param beanClass Bean 类型。
     * @param constructor 构造方法。
     * @param order Bean 的顺序。
     * @param primary 是否标识为 @Primary。
     * @param initMethodName 初始化方法名。
     * @param destroyMethodName 销毁方法名。
     * @param initMethod 初始化方法对象。
     * @param destroyMethod 销毁方法对象。
     */
    public BeanDefinition(String name, Class<?> beanClass, Constructor<?> constructor, int order, boolean primary, String initMethodName,
                          String destroyMethodName, Method initMethod, Method destroyMethod) {
        this.name = name;
        this.beanClass = beanClass;
        this.constructor = constructor;
        this.factoryName = null;
        this.factoryMethod = null;
        this.order = order;
        this.primary = primary;
        constructor.setAccessible(true);
        setInitAndDestroyMethod(initMethodName, destroyMethodName, initMethod, destroyMethod);
    }

    /**
     * Bean注解创建的BeanDefinition
     * @param name Bean 名称。
     * @param beanClass Bean 类型。
     * @param factoryName 工厂方法名称。
     * @param factoryMethod 工厂方法对象。
     * @param order Bean 的顺序。
     * @param primary 是否标识为 @Primary。
     * @param initMethodName 初始化方法名。
     * @param destroyMethodName 销毁方法名。
     * @param initMethod 初始化方法对象。
     * @param destroyMethod 销毁方法对象。
     */
    public BeanDefinition(String name, Class<?> beanClass, String factoryName, Method factoryMethod, int order, boolean primary, String initMethodName,
                          String destroyMethodName, Method initMethod, Method destroyMethod) {
        this.name = name;
        this.beanClass = beanClass;
        this.constructor = null;
        this.factoryName = factoryName;
        this.factoryMethod = factoryMethod;
        this.order = order;
        this.primary = primary;
        factoryMethod.setAccessible(true);
        setInitAndDestroyMethod(initMethodName, destroyMethodName, initMethod, destroyMethod);
    }

    /**
     * 设置初始化/销毁方法
     * @param initMethodName 初始化方法名。
     * @param destroyMethodName 销毁方法名。
     * @param initMethod 初始化方法对象。
     * @param destroyMethod 销毁方法对象。
     */
    private void setInitAndDestroyMethod(String initMethodName, String destroyMethodName, Method initMethod, Method destroyMethod) {
        this.initMethodName = initMethodName;
        this.destroyMethodName = destroyMethodName;
        if (initMethod != null) {
            initMethod.setAccessible(true);
        }
        if (destroyMethod != null) {
            destroyMethod.setAccessible(true);
        }
        this.initMethod = initMethod;
        this.destroyMethod = destroyMethod;
    }

    @Nullable
    public Constructor<?> getConstructor() {
        return this.constructor;
    }

    @Nullable
    public String getFactoryName() {
        return this.factoryName;
    }

    @Nullable
    public Method getFactoryMethod() {
        return this.factoryMethod;
    }

    @Nullable
    public Method getInitMethod() {
        return this.initMethod;
    }

    @Nullable
    public Method getDestroyMethod() {
        return this.destroyMethod;
    }

    @Nullable
    public String getInitMethodName() {
        return this.initMethodName;
    }

    @Nullable
    public String getDestroyMethodName() {
        return this.destroyMethodName;
    }

    public String getName() {
        return this.name;
    }

    public Class<?> getBeanClass() {
        return this.beanClass;
    }

    @Nullable
    public Object getInstance() {
        return this.instance;
    }

    /**
     * 返回 Bean 的实例对象，如果实例为 null 则抛出 BeanCreationException。
     * @return Bean实例。
     */
    public Object getRequiredInstance() {
        if (this.instance == null) {
            throw new BeanCreationException(String.format("Instance of bean with name '%s' and type '%s' is not instantiated during current stage.",
                    this.getName(), this.getBeanClass().getName()));
        }
        return this.instance;
    }

    /**
     * 设置 Bean 的实例。确保实例不为 null，并且实例类型与 beanClass 兼容。否则会抛出 BeanCreationException。
     * @param instance Bean实例。
     */
    public void setInstance(Object instance) {
        Objects.requireNonNull(instance, "Bean instance is null.");
        if (!this.beanClass.isAssignableFrom(instance.getClass())) {
            throw new BeanCreationException(String.format("Instance '%s' of Bean '%s' is not the expected type: %s", instance, instance.getClass().getName(),
                    this.beanClass.getName()));
        }
        this.instance = instance;
    }

    /**
     * 是否标识为 @Primary。
     * @return 是否标识为 @Primary。
     */
    public boolean isPrimary() {
        return this.primary;
    }

    @Override
    public String toString() {
        return "BeanDefinition [name=" + name + ", beanClass=" + beanClass.getName() + ", factory=" + getCreateDetail() + ", init-method="
                + (initMethod == null ? "null" : initMethod.getName()) + ", destroy-method=" + (destroyMethod == null ? "null" : destroyMethod.getName())
                + ", primary=" + primary + ", instance=" + instance + "]";
    }

    String getCreateDetail() {
        if (this.factoryMethod != null) {
            String params = String.join(", ", Arrays.stream(this.factoryMethod.getParameterTypes()).map(t -> t.getSimpleName()).toArray(String[]::new));
            return this.factoryMethod.getDeclaringClass().getSimpleName() + "." + this.factoryMethod.getName() + "(" + params + ")";
        }
        return null;
    }

    /**
     * 排序规则：首先根据 order 进行比较，order 越小优先级越高；如果 order 相同，则根据 name 的字典顺序进行比较。
     * @param def 比较的 BeanDefinition 对象。
     * @return 比较结果。
     */
    @Override
    public int compareTo(BeanDefinition def) {
        int cmp = Integer.compare(this.order, def.order);
        if (cmp != 0) {
            return cmp;
        }
        return this.name.compareTo(def.name);
    }
}
